/*
 * eforce.c
 *
 *  Created on: 2011-8-1
 *      Author: "Tao Guo<g@ur9.org>"
 */


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pwm.h>
#include <linux/slab.h>

#include "eforce.h"

#define EFORCE_DEBUG_ENABLE		1

#if EFORCE_DEBUG_ENABLE
#define DRV_SAY printk
#else
#define DRV_SAY(...)
#endif

#define DEVICE_NAME 		"eforce"
#define DRV_VERSION			"0.2.1"
#define EFORCE_PWM0_ID		2
#define EFORCE_PWM1_ID		3

#define EFORCE_DIR0_GPIO0		48
#define EFORCE_DIR0_GPIO1		49
#define EFORCE_DIR1_GPIO0		50
#define EFORCE_DIR1_GPIO1		51
#define EFORCE_BRAKE_GPIO		54

#define EFORCE_PERIOD		100000
struct eforce_data_s {
	struct pwm_device	*pwm0;
	struct pwm_device	*pwm1;
	unsigned int		dir0_0;
	unsigned int		dir0_1;
	unsigned int		dir1_0;
	unsigned int		dir1_1;
	unsigned int		brake;
}  *ef_data;

void eforce_pwm_set(struct pwm_device *pwm, unsigned int value)
{
	unsigned int period = EFORCE_PERIOD;
	unsigned int max = EFORCE_PWM_MAX;

	if (value == 0) {
		pwm_config(pwm, 0, period);
		pwm_disable(pwm);
	} else {
		pwm_config(pwm, value * period / max, period);
		pwm_enable(pwm);
	}
}
static long eforce_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

	int err;
	__u32 value = 0;
	__u32 tmp;
	if (_IOC_TYPE(cmd) != EFORCE_MAGIC) {
		printk("error magic number\n");
		return -ENOTTY;
	}
	err = access_ok(VERIFY_READ, (void __user*) arg, _IOC_SIZE(cmd));
	if (!err) {
		printk("error access\n");
		return -EFAULT;
	}
	switch(cmd) {
	case EFORCE_SET_PWM0:
	case EFORCE_SET_PWM1:
	case EFORCE_SET_DIR0:
	case EFORCE_SET_DIR1:
		err = __get_user(value, (__u32 __user*) arg);
		break;
	default:
		err = 0;
		break;
	}
	if (err) {
		printk("get value failed\n");
		return -EFAULT;
	}
	switch(cmd) {
		case EFORCE_SET_PWM0:
			DRV_SAY("EFORCE_SET_PWM0 %d\n", value);
			eforce_pwm_set(ef_data->pwm0, value);
			break;
		case EFORCE_SET_PWM1:
			DRV_SAY("EFORCE_SET_PWM1 %d\n", value);
			eforce_pwm_set(ef_data->pwm1, value);
			break;
		case EFORCE_SET_DIR0:
			value &= 1;
			DRV_SAY("EFORCE_SET_DIR0 %d\n", value);
			tmp = 0;
			gpio_set_value(ef_data->dir0_0, tmp);
			gpio_set_value(ef_data->dir0_1, tmp);
			gpio_set_value(ef_data->dir0_0, value);
			value = (value - 1) & 1;
			gpio_set_value(ef_data->dir0_1, value);
			break;
		case EFORCE_SET_DIR1:
			value &= 1;
			DRV_SAY("EFORCE_SET_DIR1 %d\n", value);
			tmp = 0;
			gpio_set_value(ef_data->dir1_0, tmp);
			gpio_set_value(ef_data->dir1_1, tmp);
			gpio_set_value(ef_data->dir1_0, value);
			value = (value - 1) & 1;
			gpio_set_value(ef_data->dir1_1, value);
			break;
		case EFORCE_BRAKE:
			DRV_SAY("EFORCE_BRAKE %d\n", value);
			value = (~value) & 1;
			gpio_set_value(ef_data->brake, value);
			break;
		default:
			return -EINVAL;
	}
	return 0;
}

static struct file_operations dev_fops = {
    .owner	=	THIS_MODULE,
    .unlocked_ioctl	=	eforce_ioctl
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret = 0;
	printk("Loading EForce Driver V%s\n", DRV_VERSION);

	//initialize device data
	ef_data =(struct eforce_data_s *)kzalloc(sizeof(struct eforce_data_s),	GFP_KERNEL);
	if (!ef_data) {
		ret = -ENOMEM;
		goto error;
	}
	ef_data->dir0_0 = EFORCE_DIR0_GPIO0;
	ef_data->dir0_1 = EFORCE_DIR0_GPIO1;
	ef_data->dir1_0 = EFORCE_DIR1_GPIO0;
	ef_data->dir1_1 = EFORCE_DIR1_GPIO1;
	ef_data->brake = EFORCE_BRAKE_GPIO;

	//initialize PWM0
	ef_data->pwm0 = pwm_request(EFORCE_PWM0_ID, "EForce PWM0");
	if (IS_ERR(ef_data->pwm0)) {
		ret = PTR_ERR(ef_data->pwm0);
		printk("Unable to request PWM0 %d\n", EFORCE_PWM0_ID);
		goto error1;
	}
	eforce_pwm_set(ef_data->pwm0, 0);

	//initialize PWM1
	ef_data->pwm1 = pwm_request(EFORCE_PWM1_ID, "EForce PWM1");
	if (IS_ERR(ef_data->pwm1)) {
		ret = PTR_ERR(ef_data->pwm1);
		printk("Unable to request PWM1 %d\n", EFORCE_PWM1_ID);
		goto error2;
	}
	eforce_pwm_set(ef_data->pwm1, 0);

	//initialize DIR0_GPIO0
	ret = gpio_request(ef_data->dir0_0, "EForce Direction IO0-0");
	if (ret) {
		goto error3;
	}
	gpio_direction_output(ef_data->dir0_0, GPIOF_INIT_LOW);
	gpio_set_value(ef_data->dir0_0, 0);

	//initialize DIR0_GPIO1
	ret = gpio_request(ef_data->dir0_1, "EForce Direction IO0-1");
	if (ret) {
		goto error4;
	}
	gpio_direction_output(ef_data->dir0_1, GPIOF_INIT_LOW);
	gpio_set_value(ef_data->dir0_1, 0);

	//initialize DIR1_GPIO0
	ret = gpio_request(ef_data->dir1_0, "EForce Direction IO1-0");
	if (ret) {
		goto error5;
	}
	gpio_direction_output(ef_data->dir1_0, GPIOF_INIT_LOW);
	gpio_set_value(ef_data->dir1_0, 0);

	//initialize DIR1_GPIO1
	ret = gpio_request(ef_data->dir1_1, "EForce Direction IO1-1");
	if (ret) {
		goto error6;
	}
	gpio_direction_output(ef_data->dir1_1, GPIOF_INIT_LOW);
	gpio_set_value(ef_data->dir1_1, 0);

	//initialize BRAKE
	ret = gpio_request(ef_data->brake, "EForce Brake IO");
	if (ret) {
		goto error7;
	}
	gpio_direction_output(ef_data->brake, GPIOF_INIT_LOW);
	gpio_set_value(ef_data->brake, 0);

	//register misc driver
	ret = misc_register(&misc);
	if (ret) {
		printk("Register misc driver error\n");
		goto error8;
	}
	printk ("EForce Driver V%s load Successfully\n", DRV_VERSION);

	return 0;
error8:
	gpio_free(ef_data->brake);
error7:
	gpio_free(ef_data->dir0_1);
error6:
	gpio_free(ef_data->dir0_0);
error5:
	gpio_free(ef_data->dir0_1);
error4:
	gpio_free(ef_data->dir0_0);
error3:
	pwm_free(ef_data->pwm1);
error2:
	pwm_free(ef_data->pwm0);
error1:
	kfree(ef_data);
error:
	return ret;
}

static void __exit dev_exit(void)
{
	eforce_pwm_set(ef_data->pwm0, 0);
	eforce_pwm_set(ef_data->pwm1, 0);

	misc_deregister(&misc);
	gpio_free(ef_data->dir0_0);
	gpio_free(ef_data->dir0_1);
	gpio_free(ef_data->dir1_0);
	gpio_free(ef_data->dir1_1);
	gpio_free(ef_data->brake);
	pwm_free(ef_data->pwm1);
	pwm_free(ef_data->pwm0);

	kfree(ef_data);
	printk("EForce Driver Remove Successfully\n");
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EForce team");
